package show.me.the.money.servlet;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.*;
import org.codehaus.jackson.map.ObjectMapper;
import org.dom4j.Document;
import org.dom4j.Element;

import org.springframework.web.context.support.WebApplicationContextUtils;

import show.me.the.money.accredit.User;
import show.me.the.money.accredit.condition.Condition;
import show.me.the.money.accredit.result.AuthorizeResult;
import show.me.the.money.dictionary.Dictionaries;
import show.me.the.money.dictionary.Dictionary;
import show.me.the.money.dictionary.DictionaryItem;
import show.me.the.money.util.ConfigValueParser;
import show.me.the.money.util.context.Context;
import show.me.the.money.util.context.UserContext;

@SuppressWarnings("unchecked")
public class DictionaryLoader extends HttpServlet implements Servlet {;
	private static final long serialVersionUID = -8260076694457868118L;
	private static Log logger = LogFactory.getLog(DictionaryLoader.class);
	private static ObjectMapper jsonMapper =  new ObjectMapper();
	private static Pattern pattern = Pattern.compile("([^/]+)\\.dic");
	public void init(ServletConfig oCfg) throws ServletException {
		WebApplicationContextUtils.getRequiredWebApplicationContext(oCfg.getServletContext());
		logger.info("Servlet init");
	}
	
	private String getDicId(String url){
		Matcher matcher = pattern.matcher(url);
		if(matcher.find()){
	        return matcher.group(1);
		}
		return null;
	}
	
	public void doGet(HttpServletRequest request, HttpServletResponse response){
		doDicService(request,response);
	}
	
	public void doPost(HttpServletRequest request, HttpServletResponse response){
		doDicService(request,response);	
	}
	
	private void doDicService(HttpServletRequest request, HttpServletResponse response){
		String id = getDicId(request.getRequestURI());

		if(StringUtils.isEmpty(id)){
			logger.error("Request Url's arg [dic id] missing");
			return;
		}
		HashMap<String,String> query = parseQueryString(request.getQueryString());
		
		String parentKey = query.get("parentKey");
		if(parentKey == null){
			parentKey = "";
		}
		Context ctx = initContext(request);
		if(parentKey.startsWith("%")){
			parentKey = ConfigValueParser.parse(parentKey,ctx);
		}
		
		HashMap<String,Object> json = new HashMap<String,Object>();
		if(id.equals("dictionaries")){
			json = getList();
		}
		else{
			int sliceType = Integer.parseInt(query.get("sliceType"));
			String queryText = null;
			int start = 0;
			int limit = 0;
			String s = request.getParameter("start");
			String l = request.getParameter("limit");
			start = s == null ? 0 : Integer.parseInt(s);
			limit = l == null ? 0 : Integer.parseInt(l);
			if(request.getMethod().toLowerCase().equals("post")){
				queryText = request.getParameter("query");
			}
			else{
				queryText = query.get("query");
			}
			Dictionary dic = Dictionaries.instance().getDic(id);
			if(dic == null){
				logger.error("dic[" + id + "] not found");
				response.setStatus(404);
				return;
			}
			List jsonItems = new ArrayList();
			json.put("id", id);
			json.put("parentKey", parentKey);
			json.put("sliceType", sliceType);
			json.put("queryText", queryText);
			json.put("result", 20);
			json.put("items", jsonItems);

			List<DictionaryItem> ls = dic.getSlice(parentKey,sliceType,queryText,start,limit);
			if(ls == null){
				logger.error("dic[" + id + "] no items or load failed");
				response.setStatus(500);
				return;
			}
			//pocess authorize;
			String src = query.get("src");
			Condition limitCnd = null;
			if(src != null){
				User user = (User)ctx.get("user.instance");
				if(user != null){
					AuthorizeResult result = user.authorize("storage",src);
					if(result.getResult() == AuthorizeResult.UNDERCONDITION){
						limitCnd = result.getCondition("limit");
					}
				}
			}
			
			boolean isNav = dic.getProperty("class").equals("ComposeDictionary");
			for(Iterator<DictionaryItem> i = ls.iterator();i.hasNext();){
				DictionaryItem it = i.next();
				if(limitCnd != null){
					ctx.put("value", it.getKey());
					if((Boolean)limitCnd.run(ctx)){
						continue;
					};
				}				
				HashMap<String,Object> o = it.data();
				if(!isNav){
					o.remove("nav");
				}
				jsonItems.add(o);
			}
			if(!StringUtils.isEmpty(parentKey)){
				DictionaryItem it = dic.getItem(parentKey);
				if(it != null){
					json.put("parentItem",it.data());
				}
			}
		}
		
		try {
			writeToResponse(response,json);
		} 
		catch (IOException e) {
			logger.error("writeToResponse IO Exception:", e);
		}
		
	}
	
	public HashMap<String,Object> getList(){
		Document defineDoc = Dictionaries.instance().getDefineDoc();
		Element define = defineDoc.getRootElement();
		List items = new ArrayList();
		HashMap<String,Object> res = new HashMap<String,Object>();
		for(Iterator<Element> it = define.elementIterator("dic");it.hasNext();){
			Element dic = it.next();
			HashMap<String,Object> o = new HashMap<String,Object>();;
			
			String cls = dic.attributeValue("class","XMLDictionary");
			if(!cls.equals("XMLDictionary")){
				continue;
			}
			if(dic.attributeValue("file", "").startsWith("sys")){
				continue;
			}
			if(dic.attributeValue("lock") != null){
				if(dic.attributeValue("lock").equals("true")){
					continue;
				}
			}
			String dicId = dic.attributeValue("id");
			Dictionary dictionary = Dictionaries.instance().getDic(dicId);
			if(dictionary == null){
				logger.error("["+ dicId +"]dic is not exist");
				continue;
			}
			Document d = dictionary.getDefineDoc();
			if(d!=null){
				String codeRule = d.getRootElement().attributeValue("codeRule", "");
				o.put("codeRule",codeRule);
			}
			o.put("key",dic.attributeValue("id"));
			o.put("text",dic.attributeValue("alias"));
			o.put("class", dic.attributeValue("class"));
			o.put("type", "dic");
			o.put("folder","true");
			
			items.add(o);
		}
		res.put("items", items);
		return res;
	}
	
	public HashMap<String,String> parseQueryString(String q){

		HashMap<String,String> query = new  HashMap<String,String>();
		query.put("parentKey", null);
		query.put("query", null);
		query.put("sliceType", String.valueOf(Dictionary.SLICE_ALL_LEAF));
		if(q != null){
			int p = q.indexOf("&");
			if(p > -1){
				String[] qus = q.split("&");
				int size = qus.length;
					for(int i = 0; i < size; i ++){
						String[] kv = qus[i].split("=");
						if(kv.length == 2){
							try {
								String key = URLDecoder.decode(kv[0],"utf-8");
								String value = URLDecoder.decode(kv[1],"utf-8");
								
								query.put(key,value);
							} 
							catch (UnsupportedEncodingException e) {
								logger.error("parseQueryStringFailed:", e);
							}
						}
					}
			}
			else{
				String[] kv = q.split("=");
				if(kv.length == 2){
					try {
						String key = URLDecoder.decode(kv[0],"utf-8");
						String value = URLDecoder.decode(kv[1],"utf-8");
						query.put(key,value);
					} 
					catch (UnsupportedEncodingException e) {
						logger.error("parseQueryStringFailed:", e);
					}
				}
			}
		}
		return query;
	}
	
	public void writeToResponse(HttpServletResponse response,HashMap<String,Object> json) throws IOException{
		response.addHeader("content-type","text/javascript");
		OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream(),"utf-8");
		jsonMapper.writeValue(out, json);
	}
	
	public static Context initContext(HttpServletRequest request){
		Context ctx = Dispatcher.createContext(request);
		User user = Dispatcher.getUser(request);
		Context userCtx = new UserContext(user);
		ctx.putCtx("user", userCtx);	
		return ctx;
	}
	
}